#pragma once

#include "SmallObjectAllocator.h"
#include "MemoryTracker.h"
#include "LockPolicy.h"
#include "TLSFAllocator.h"

#ifdef _DEBUG
	#define MEMORY_TRACKING
#endif

namespace MemMgr
{

template
<
	class ThreadPolicy = NoLock
>
class AllocationPolicy : public ThreadPolicy
{
public:
	static void* Allocate(size_t iSize, const char* iFilename, const char* iFunctionName, int iLine, const std::string iCategory)
	{
		ThreadPolicy lock;
		void* ptr;

		if(iSize > SmallObjectAllocator::GetSingleton().GetMaxObjectSize())
		{
			 ptr = TLSFAllocator::GetSingleton().Allocate(iSize);
		} 
		else
		{
			ptr = SmallObjectAllocator::GetSingleton().Allocate(iSize);
		}

#ifdef MEMORY_TRACKING
		MemoryTracker::GetSingleton().RegisterAlloc(ptr, iSize, iFilename, iFunctionName, iLine, iCategory);
#endif

		return ptr;
	}

	static void* BigAllocate(size_t iSize)	// NOT TRACKED
	{
		ThreadPolicy lock;

		void* ptr = TLSFAllocator::GetSingleton().Allocate(iSize);

		return ptr;
	}

	static void BigDeallocate(void* iPtr)	// NOT TRACKED
	{
		ThreadPolicy lock;

		TLSFAllocator::GetSingleton().Deallocate(iPtr);
	}

	static void Deallocate(void* iPtr)
	{
		ThreadPolicy lock;
#ifdef MEMORY_TRACKING
		MemoryTracker::GetSingleton().UnregisterAlloc(iPtr);
#endif
		if(!SmallObjectAllocator::GetSingleton().Deallocate(iPtr))
		{	
			TLSFAllocator::GetSingleton().Deallocate(iPtr);
		}
	}

	static void Deallocate(void* iPtr, size_t iSize)
	{
		ThreadPolicy lock;
#ifdef MEMORY_TRACKING
		MemoryTracker::GetSingleton().UnregisterAlloc(iPtr);
#endif
		if(iSize > SmallObjectAllocator::GetSingleton().GetMaxObjectSize())
		{
			TLSFAllocator::GetSingleton().Deallocate(iPtr);
		} 
		else
		{
			SmallObjectAllocator::GetSingleton().Deallocate(iPtr, iSize);
		}
	}
};

typedef AllocationPolicy<NoLock> Policy;

template<class T>
inline static T* AllocateA(size_t iNum, size_t iSize, const char* iFilename, const char* iFunctionName, int iLine, const std::string iCategory = "General")
{
	void* ptr = Policy::Allocate(iSize*iNum + sizeof(size_t), iFilename, iFunctionName, iLine, iCategory);
	*(static_cast<size_t*>(ptr)) = iNum;

	T* arrayPtr = static_cast<T*>(static_cast<void*>(static_cast<char*>(ptr) + sizeof(size_t)));

	for(int i=0; i<iNum; ++i)
	{
		new (arrayPtr+i) T;
	}

	return arrayPtr;
}

inline static void* Allocate(size_t iSize, const char* iFilename, const char* iFunctionName, int iLine, const std::string iCategory = "General")
{
	return Policy::Allocate(iSize, iFilename, iFunctionName, iLine, iCategory);
}

template<class T>
inline static void Deallocate(T* iPtr, size_t iSize)
{
	iPtr->~T();
	Policy::Deallocate(iPtr, iSize);
}

template<class T>
inline static void Deallocate(T* iPtr)
{
	Policy::Deallocate(iPtr);
}

template<class T>
inline static void Delete(T* iPtr)
{
	iPtr->~T();
	Deallocate(iPtr);
}

template<class T>
inline static void DeleteA(T* iPtr)
{
	void* hPtr = static_cast<char*>(static_cast<void*>(iPtr)) - sizeof(size_t);
	size_t num = *(static_cast<size_t*>(hPtr));
	for(int i=0; i<num; ++i)
	{
		(iPtr + i)->~T();
	}
	Deallocate(hPtr);
}


inline static void* BigAllocate(size_t iSize)
{
	return Policy::BigAllocate(iSize);
}

inline static void BigDeallocate(void* iPtr)
{
	Policy::BigDeallocate(iPtr);
}

}